import sqlite3

from datetime import datetime

import click

from flask import current_app, g

def get_db():
    # g 对象会在 ​Flask 处理每个 HTTP 请求时自动创建
    '''
        ​g 是 Flask 提供的一个全局请求上下文对象​（flask.g），全称是 global 或 globals 的缩写。
        它不是真正的全局变量，而是请求级别的临时存储，即每个请求都会有一个独立的 g 对象。
        它的生命周期和当前 HTTP 请求绑定，请求结束后会自动销毁。
        存储连接的相关资源
    '''
    if 'db' not in g:
        g.db = sqlite3.connect(
            current_app.config['DATABASE'],
            detect_types=sqlite3.PARSE_DECLTYPES # 启用 SQLite 的类型转换功能 （sql 类型数据 -- > Python 数据类型转换）
        )
        g.db.row_factory = sqlite3.Row # 告诉数据库，返回的行对象应该以字典形式返回
    # sqlite3.connect ：建立一个连接对象，连接到配置键 DATABASE 指向的文件。这个数据库还不存在，直到你之后初始化数据库才会被创建。
    
    return g.db

def close_db(e=None):
    '''
        关闭数据库连接
    '''
    db = g.pop('db', None)

    if db is not None:
        db.close()

def init_db():
    db = get_db() # 获取数据库连接

    # 打开一个相对于 flaskr 包的文件
    with current_app.open_resource('schema.sql') as f:    
        db.executescript(f.read().decode('utf8'))

# 注册命令
@click.command('init-db')
def init_db_command():
    '''
        初始化数据库
    '''
    init_db()
    click.echo('Initialized the database.')
    
# 为 SQLite 数据库注册一个自定义的类型转换器，专门用于将数据库中的 timestamp 类型字段自动转换为 Python 的 datetime 对象。
sqlite3.register_converter(
    "timestamp",  # SQLite 中的类型名称
    lambda v: datetime.fromisoformat(v.decode())  # 转换函数：v 是二进制数据，v.decode() 是将二进制数据转换为字符串
)

def init_app(app):
    # 需要被应用实例捕捉（APP），即注册到应用实例中
    # app实例创建后，通过调用 init_db 方法，成功注册
    app.teardown_appcontext(close_db)
    app.cli.add_command(init_db_command)
'''
1️. app.teardown_appcontext : 类似触发器

​何时触发：
​HTTP 请求结束：在 Web 请求完成后，Flask 会自动销毁应用上下文，触发 close_db。
​CLI 命令结束：执行完 Flask CLI 命令后也会清理上下文。

2. app.cli.add_command ： ​向 Flask 的 CLI（命令行接口）添加一个自定义命令 —— 允许通过 flask init—db 命令初始化数据库。

'''